package com.kancy.kie.server.client.service;

import com.kancy.kie.server.client.properties.DroolsKieServerProperties;
import org.kie.api.KieServices;
import org.kie.api.command.Command;
import org.kie.api.command.KieCommands;
import org.kie.api.runtime.ExecutionResults;
import org.kie.server.api.model.KieServiceResponse;
import org.kie.server.api.model.ServiceResponse;
import org.kie.server.client.RuleServicesClient;
import org.kie.server.common.rest.NoEndpointFoundException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

import java.util.LinkedList;
import java.util.List;
import java.util.Objects;

/**
 * <p>
 * DroolsRemoteClientImpl
 * <p>
 *
 * @author: kancy
 * @date: 2020/3/12 14:03
 **/

public class SimpleDroolsRuleEngine implements DroolsRuleEngine {
    private static final Logger log = LoggerFactory.getLogger(SimpleDroolsRuleEngine.class);

    private final RuleServicesClient ruleServicesClient;

    private final DroolsKieServerProperties droolsKieServerProperties;

    public SimpleDroolsRuleEngine(RuleServicesClient ruleServicesClient,
                                  DroolsKieServerProperties droolsKieServerProperties) {
        this.ruleServicesClient = ruleServicesClient;
        this.droolsKieServerProperties = droolsKieServerProperties;
    }

    /**
     * 调用规则引擎
     *
     * @param params 参数
     */
    @Override
    public RuleResults call(RuleParams params) {
        KieCommands cmdFactory = KieServices.Factory.get().getCommands();
        List<Command<?>> commands = new LinkedList<>();
        params.getInsertMap().entrySet().stream().forEach(e ->{
            commands.add(cmdFactory.newInsert(e.getValue(), e.getKey()));
        });
        params.getGlobalMap().entrySet().stream().forEach(e ->{
            commands.add(cmdFactory.newSetGlobal(e.getKey(), e.getValue(), true));
        });
        commands.add(cmdFactory.newFireAllRules());
        return call(params.getContainerId(), params.getSessionName(), commands);
    }

    /**
     * 调用规则引擎
     *
     * @param commands 参数
     * @param containerId 容器id
     * @param sessionName session名称
     */
    @Override
    public RuleResults call(String containerId, String sessionName, List<Command<?>> commands) {
        Assert.hasText(containerId, "ContainerId is empty.");
        Assert.notEmpty(commands, "Commands is empty.");
        return retryCall(containerId, sessionName, commands, 0);
    }

    /**
     * 重试调用
     * @param containerId
     * @param sessionName
     * @param commands
     * @param retry
     * @return
     */
    private RuleResults retryCall(String containerId, String sessionName, List<Command<?>> commands, int retry) {
        RuleResults ruleResult = new RuleResults();
        ServiceResponse<ExecutionResults> results = null;
        try {
            KieCommands cmdFactory = KieServices.Factory.get().getCommands();
            List<Command<?>> newCommands = new LinkedList<>();
            newCommands.addAll(commands);
            results = ruleServicesClient.executeCommandsWithResults(containerId,
                    StringUtils.hasText(sessionName) ?
                            cmdFactory.newBatchExecution(newCommands, sessionName):
                            cmdFactory.newBatchExecution(newCommands));
            // 失败重试
            if(droolsKieServerProperties.isFailureRetry() &&
                    Objects.equals(KieServiceResponse.ResponseType.FAILURE, results.getType())){
                if (retry < droolsKieServerProperties.getMaxRetry()){
                    retry++;
                    log.info("调用规则[{}-{}}]引擎失败，即将开始第【{}】次重试：{}", containerId, sessionName, retry, results.getMsg());
                    return retryCall(containerId, sessionName, commands, retry);
                }
            }
            ruleResult.setCode(results.getType().toString());
            ruleResult.setMsg(results.getMsg());
            ruleResult.setSuccess(Objects.equals(KieServiceResponse.ResponseType.SUCCESS, results.getType()));
            ruleResult.setExecutionResults(results.getResult());
        } catch (NoEndpointFoundException e) {
            // 服务不可用时，进行重试
            if (retry < droolsKieServerProperties.getMaxRetry()){
                retry++;
                log.info("调用规则[{}-{}}]引擎失败，即将开始第【{}】次重试：{}", containerId, sessionName, retry, e.getMessage());
                return retryCall(containerId, sessionName, commands, retry);
            }
            handleCallException(containerId, sessionName, ruleResult, e);
        } catch (Exception e) {
            handleCallException(containerId, sessionName, ruleResult, e);
        }
        return ruleResult;
    }

    /**
     * 处理调用异常
     * @param containerId
     * @param sessionName
     * @param ruleResult
     * @param e
     */
    private void handleCallException(String containerId, String sessionName, RuleResults ruleResult, Exception e) {
        e.printStackTrace();
        String errorMessage = String.format("调用规则[%s-%s]引擎失败：%s",  containerId, sessionName, e.getMessage());
        log.error(errorMessage);
        ruleResult.setCode(KieServiceResponse.ResponseType.FAILURE.toString());
        ruleResult.setMsg(errorMessage);
    }
}
